package com.ruoyi.flow.common.tools.mediaconverter.converter.reflections.util;


import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.ruoyi.flow.common.tools.mediaconverter.converter.reflections.vfs.Vfs;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;

import static com.ruoyi.flow.common.tools.mediaconverter.converter.reflections.ReflectionUtils.forName;

/** */
public class JavaReflectionUtils {

	public static List<Field> getFields(Class cls) {
		return Lists.newArrayList(cls.getDeclaredFields());
	}

	public static List<Member> getMethods(Class cls) {
		List<Member> methods = Lists.newArrayList();
		methods.addAll(Arrays.asList(cls.getDeclaredMethods()));
		methods.addAll(Arrays.asList(cls.getDeclaredConstructors()));
		return methods;
	}

	public static String getMethodName(Member method) {
		return method instanceof Method ? method.getName() : method instanceof Constructor ? "<init>" : null;
	}

	public static List<String> getParameterNames(final Member member) {
		List<String> result = Lists.newArrayList();

		Class<?>[] parameterTypes = member instanceof Method ? ((Method) member).getParameterTypes()
				: member instanceof Constructor ? ((Constructor) member).getParameterTypes() : null;

		if (parameterTypes != null) {
			for (Class<?> paramType : parameterTypes) {
				String name = getName(paramType);
				result.add(name);
			}
		}

		return result;
	}

	public static List<String> getClassAnnotationNames(Class aClass) {
		return getAnnotationNames(aClass.getDeclaredAnnotations());
	}

	public static List<String> getFieldAnnotationNames(Field field) {
		return getAnnotationNames(field.getDeclaredAnnotations());
	}

	public static List<String> getMethodAnnotationNames(Member method) {
		Annotation[] annotations = method instanceof Method ? ((Method) method).getDeclaredAnnotations()
				: method instanceof Constructor ? ((Constructor) method).getDeclaredAnnotations() : null;
		return getAnnotationNames(annotations);
	}

	public static List<String> getParameterAnnotationNames(Member method, int parameterIndex) {
		Annotation[][] annotations = method instanceof Method ? ((Method) method).getParameterAnnotations()
				: method instanceof Constructor ? ((Constructor) method).getParameterAnnotations() : null;

		return getAnnotationNames(annotations != null ? annotations[parameterIndex] : null);
	}

	public static String getReturnTypeName(Member method) {
		return ((Method) method).getReturnType().getName();
	}

	public static String getFieldName(Field field) {
		return field.getName();
	}

	public static Class getOrCreateClassObject(Vfs.File file) throws Exception {
		return getOrCreateClassObject(file, null);
	}

	public static Class getOrCreateClassObject(Vfs.File file, ClassLoader... loaders) throws Exception {
		String name = file.getRelativePath().replace("/", ".").replace(".class", "");
		return forName(name, loaders);
	}

	public static String getMethodModifier(Member method) {
		return Modifier.toString(method.getModifiers());
	}

	public static String getMethodKey(Class cls, Member method) {
		return getMethodName(method) + "(" + Joiner.on(", ").join(getParameterNames(method)) + ")";
	}

	public static String getMethodFullKey(Class cls, Member method) {
		return getClassName(cls) + "." + getMethodKey(cls, method);
	}

	public static boolean isPublic(Object o) {
		Integer mod = o instanceof Class ? ((Class) o).getModifiers()
				: o instanceof Member ? ((Member) o).getModifiers() : null;

		return mod != null && Modifier.isPublic(mod);
	}

	public static String getClassName(Class cls) {
		return cls.getName();
	}

	public static String getSuperclassName(Class cls) {
		Class superclass = cls.getSuperclass();
		return superclass != null ? superclass.getName() : "";
	}

	public static List<String> getInterfacesNames(Class cls) {
		Class[] classes = cls.getInterfaces();
		List<String> names = new ArrayList<String>(classes != null ? classes.length : 0);
		if (classes != null)
			for (Class cls1 : classes)
				names.add(cls1.getName());
		return names;
	}

	public static boolean acceptsInput(String file) {
		return file.endsWith(".class");
	}

	//
	private static List<String> getAnnotationNames(Annotation[] annotations) {
		List<String> names = new ArrayList<String>(annotations.length);
		for (Annotation annotation : annotations) {
			names.add(annotation.annotationType().getName());
		}
		return names;
	}

	public static String getName(Class type) {
		if (type.isArray()) {
			try {
				Class cl = type;
				int dim = 0;
				while (cl.isArray()) {
					dim++;
					cl = cl.getComponentType();
				}
				return cl.getName() + Strings.repeat("[]", dim);
			} catch (Throwable e) {
				//
			}
		}
		return type.getName();
	}
}
